﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace CommonTools.UserControls
{
    public class CyclicTick : UserControl
    {
        Pen backpen;

        public double Size
        {
            get { return (double)GetValue(SizeProperty); }
            set { SetValue(SizeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SizeProperty =
            DependencyProperty.Register("Size", typeof(double), typeof(CyclicTick), new PropertyMetadata(100.0, new PropertyChangedCallback(RefeshTick)));



        public double StartAngle
        {
            get { return (double)GetValue(StartAngleProperty); }
            set { SetValue(StartAngleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartAngle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(CyclicTick), new PropertyMetadata(0.0, new PropertyChangedCallback(RefeshTick)));



        public double EndAngle
        {
            get { return (double)GetValue(EndAngleProperty); }
            set { SetValue(EndAngleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EndAngle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndAngleProperty =
            DependencyProperty.Register("EndAngle", typeof(double), typeof(CyclicTick), new PropertyMetadata(360.0, new PropertyChangedCallback(RefeshTick)));




        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MinValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MinValueProperty =
            DependencyProperty.Register("MinValue", typeof(double), typeof(CyclicTick), new PropertyMetadata(0.0, new PropertyChangedCallback(RefeshTick)));




        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxValueProperty =
            DependencyProperty.Register("MaxValue", typeof(double), typeof(CyclicTick), new PropertyMetadata(100.0, new PropertyChangedCallback(RefeshTick)));




        public double BigTick
        {
            get { return (double)GetValue(BigTickProperty); }
            set { SetValue(BigTickProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BigTick.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BigTickProperty =
            DependencyProperty.Register("BigTick", typeof(double), typeof(CyclicTick), new PropertyMetadata(10.0, new PropertyChangedCallback(RefeshTick)));




        public double SmallTick
        {
            get { return (double)GetValue(SmallTickProperty); }
            set { SetValue(SmallTickProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SmallTick.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SmallTickProperty =
            DependencyProperty.Register("SmallTick", typeof(double), typeof(CyclicTick), new PropertyMetadata(2.0, new PropertyChangedCallback(RefeshTick)));



        public double BigTickLength
        {
            get { return (double)GetValue(BigTickLengthProperty); }
            set { SetValue(BigTickLengthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for BigTickLength.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BigTickLengthProperty =
            DependencyProperty.Register("BigTickLength", typeof(double), typeof(CyclicTick), new PropertyMetadata(10.0, new PropertyChangedCallback(RefeshTick)));




        public double SmallTickLength
        {
            get { return (double)GetValue(SmallTickLengthProperty); }
            set { SetValue(SmallTickLengthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SmallTickLength.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SmallTickLengthProperty =
            DependencyProperty.Register("SmallTickLength", typeof(double), typeof(CyclicTick), new PropertyMetadata(5.0, new PropertyChangedCallback(RefeshTick)));



        public Brush PenBrush
        {
            get { return (Brush)GetValue(PenBrushProperty); }
            set { SetValue(PenBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PenBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PenBrushProperty =
            DependencyProperty.Register("PenBrush", typeof(Brush), typeof(CyclicTick), new PropertyMetadata(Brushes.Black, (sender, e) =>
            {
                var dp = sender as CyclicTick;
                if (dp == null) return;
                dp.RefeshPen();
            }));



        public double PenThickness
        {
            get { return (double)GetValue(PenThicknessProperty); }
            set { SetValue(PenThicknessProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PenThickness.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PenThicknessProperty =
            DependencyProperty.Register("PenThickness", typeof(double), typeof(CyclicTick), new PropertyMetadata(1.0, (sender, e) =>
            {
                var dp = sender as CyclicTick;
                if (dp == null) return;
                dp.RefeshPen();
            }));



        public new double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for FontSize.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FontSizeProperty =
            DependencyProperty.Register("FontSize", typeof(double), typeof(CyclicTick), new PropertyMetadata(8.0, new PropertyChangedCallback(RefeshTick)));




        public double TickOffset
        {
            get { return (double)GetValue(TickOffsetProperty); }
            set { SetValue(TickOffsetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TickOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TickOffsetProperty =
            DependencyProperty.Register("TickOffset", typeof(double), typeof(CyclicTick), new PropertyMetadata(0.0));





        public CyclicTick()
        {
            backpen = new Pen(PenBrush, PenThickness);
            backpen.Freeze();
            this.SizeChanged += CyclicTick_SizeChanged;
        }

        private void CyclicTick_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            InvalidateVisual();
        }

        private void RefeshPen()
        {
            backpen = new Pen(PenBrush, PenThickness);
            backpen.Freeze();
            InvalidateVisual();
        }

        private static void RefeshTick(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var dp = d as CyclicTick;
            if (dp == null) return;
            dp.InvalidateVisual();
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (this.Visibility == Visibility.Collapsed) return;
            var centerX = ActualWidth / 2;
            var centerY = ActualHeight / 2;
            FormattedText maxTxt = new FormattedText(MaxValue.ToString().PadLeft(2, '0'), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), FontSize, PenBrush);

            TickOffset = Math.Sqrt(Math.Pow(maxTxt.Width, 2) + Math.Pow(maxTxt.Height, 2)) + SmallTickLength;
            //var txtOffset = MaxValue.ToString().Length * FontSize;
            double radiusX = centerX - TickOffset;
            double radiusY = centerY - TickOffset;
            if (radiusX < 0 || radiusY < 0) return;
            PathGeometry geometry = new PathGeometry();
            var arcFigure = new PathFigure
            {
                StartPoint = new Point(centerX + radiusX * Math.Cos(StartAngle * Math.PI / 180), centerY + radiusY * Math.Sin(StartAngle * Math.PI / 180)),
                IsClosed = false,
                IsFilled = true
            };
            arcFigure.Segments.Add(new ArcSegment(new Point(centerX + radiusX * Math.Cos(EndAngle * Math.PI / 180),
                centerY + radiusY * Math.Sin(EndAngle * Math.PI / 180)), new Size(radiusX, radiusY), 0,
                Math.Abs(EndAngle - StartAngle) > 180, SweepDirection.Clockwise, true));
            geometry.Figures.Add(arcFigure);
            drawingContext.DrawGeometry(Brushes.Transparent, backpen, geometry);

            var tickStartAngle = StartAngle;
            //小刻度
            var smallTickCount = (int)((MaxValue - MinValue) / SmallTick);
            var smallAngle = (Math.Abs(StartAngle - EndAngle)) / smallTickCount;
            for (int i = 0; i <= smallTickCount; i++)
            {
                Point p1 = new Point((radiusX + SmallTickLength) * Math.Cos((tickStartAngle + i * smallAngle) * Math.PI / 180) + centerX,
                    (radiusY + SmallTickLength) * Math.Sin((tickStartAngle + i * smallAngle) * Math.PI / 180) + centerY);
                Point p2 = new Point(radiusX * Math.Cos((tickStartAngle + i * smallAngle) * Math.PI / 180) + centerX,
                    radiusY * Math.Sin((tickStartAngle + i * smallAngle) * Math.PI / 180) + centerY);
                drawingContext.DrawLine(backpen, p1, p2);
            }

            //大刻度
            var bigTickCount = (int)((MaxValue - MinValue) / BigTick);
            var bigAngle = (Math.Abs(StartAngle - EndAngle)) / bigTickCount;
            for (int i = 0; i <= bigTickCount; i++)
            {
                Point p1 = new Point((radiusX + BigTickLength) * Math.Cos((tickStartAngle + i * bigAngle) * Math.PI / 180) + centerX,
                    (radiusY + BigTickLength) * Math.Sin((tickStartAngle + i * bigAngle) * Math.PI / 180) + centerY);
                Point p2 = new Point(radiusX * Math.Cos((tickStartAngle + i * bigAngle) * Math.PI / 180) + centerX,
                    radiusY * Math.Sin((tickStartAngle + i * bigAngle) * Math.PI / 180) + centerY);
                drawingContext.DrawLine(backpen, p1, p2);
                FormattedText txt = new FormattedText((BigTick * i + MinValue).ToString(), new System.Globalization.CultureInfo("zh-CHS", false), FlowDirection.LeftToRight, new Typeface("Microsoft YaHei"), FontSize, PenBrush);
                drawingContext.DrawText(txt, new Point((centerX - txt.Width / 2) * Math.Cos((tickStartAngle + i * bigAngle) * Math.PI / 180) + centerX - txt.Width / 2,
                    (centerY - txt.Height / 2) * Math.Sin((tickStartAngle + i * bigAngle) * Math.PI / 180) + centerY - txt.Height / 2));
            }


            base.OnRender(drawingContext);
        }
    }
}
